package com.whh.parseData.parseXml.anno.helper;


import com.sun.istack.internal.NotNull;
import org.springframework.util.StringUtils;
import com.whh.parseData.parseXml.anno.Attribute;
import com.whh.parseData.parseXml.anno.Element;
import com.whh.parseData.parseXml.anno.entry.AttributeDesc;
import com.whh.parseData.parseXml.anno.entry.ElementDesc;
import com.whh.parseData.parseXml.appoint.EntryBaseInterface;
import com.whh.parseData.parseXml.utils.ClassUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 注解处理
 */
public class AnnoHelper {
    // 文件类型_元素名称_元素层级---->元素描述
    private static final Map<String, ElementDesc> NAME_ELEMENTDESC_MAP = new HashMap<String, ElementDesc>();

    /**
     *  1. 处理类上面的Element标签
     *  2. 处理类中字段上的Element标签
     *  3. 处理类中字段上的Attribute标签
     */
    public static void initBaseInfo() {
        // 获取所有实现了EntryBaseInterface的类。
        ArrayList<Class<?>> interfaceImpls = ClassUtil.getInterfaceImpls(EntryBaseInterface.class);
        for (Class<?> clazz : interfaceImpls) {
            // 处理类上的Element,File注解
            Element elementAnno = clazz.getAnnotation(Element.class);
            if (elementAnno == null) {
                System.out.println("实现该接口EntryBaseInterface，必须有注解elementAnno");
                return;
            }
            ElementDesc elementDesc = new ElementDesc();
            elementDesc.setHasChild(true);
            elementDesc.setClazz(clazz);
            elementDesc.setName(elementAnno.name());
            elementDesc.setLayer(elementAnno.layer());
            elementDesc.setFileType(elementAnno.fileType());

            // 获取该类的所有字段
            Field[] declaredFields = clazz.getDeclaredFields();
            if (declaredFields.length < 1) {
                continue;
            }

            // 字段
            for (Field declaredField : declaredFields) {
                // 处理类中的Attribute注解
                Attribute attributeAnno = declaredField.getAnnotation(Attribute.class);
                if (null != attributeAnno) {
                    AttributeDesc attributeDesc = new AttributeDesc();
                    attributeDesc.setName(attributeAnno.name());
                    declaredField.setAccessible(true);
                    attributeDesc.setField(declaredField);
                    elementDesc.addAttributeFiled(attributeDesc);
                }
                // 处理类中的Element注解
                Element childElementAnno = declaredField.getAnnotation(Element.class);
                if (null != childElementAnno) {
                    elementDesc.addNoHashChildNames(childElementAnno.name());
                    ElementDesc childElementDesc = new ElementDesc();
                    childElementDesc.setName(childElementAnno.name());
                    int childLayer = elementAnno.layer();
                    childElementDesc.setLayer(++childLayer);
                    childElementDesc.setHasChild(false);
                    childElementDesc.setClazz(null);
                    childElementDesc.setFileType(elementAnno.fileType());
                    declaredField.setAccessible(true);
                    childElementDesc.setField(declaredField);
                    putElementDesc(childElementDesc);
                }
            }

            // 获取该类的所有方法
            Method[] declaredMethods = clazz.getDeclaredMethods();
            if (declaredMethods.length < 1) {
                continue;
            }
            // 方法
            for (Method declaredMethod : declaredMethods) {
                // 处理类中的Attribute注解
                Attribute attributeAnno = declaredMethod.getAnnotation(Attribute.class);
                if (null != attributeAnno) {
                    AttributeDesc attributeDesc = new AttributeDesc();
                    attributeDesc.setName(attributeAnno.name());
                    declaredMethod.setAccessible(true);
                    attributeDesc.setMethod(declaredMethod);
                    elementDesc.addAttributeMethod(attributeDesc);
                }
                // 处理类中的Element注解
                Element childElementAnno = declaredMethod.getAnnotation(Element.class);
                if (null != childElementAnno) {
                    elementDesc.addNoHashChildNames(childElementAnno.name());
                    ElementDesc childElementDesc = new ElementDesc();
                    childElementDesc.setName(childElementAnno.name());
                    int childLayer = elementAnno.layer();
                    childElementDesc.setLayer(++childLayer);
                    childElementDesc.setHasChild(false);
                    childElementDesc.setClazz(null);
                    childElementDesc.setFileType(elementAnno.fileType());
                    declaredMethod.setAccessible(true);
                    childElementDesc.setMethod(declaredMethod);
                    putElementDesc(childElementDesc);
                }
            }
            putElementDesc(elementDesc);
        }
    }


    /**
     * 添加元素
     * @param elementDesc
     */
    public static void putElementDesc(ElementDesc elementDesc) {
        if (elementDesc == null) {
            System.out.println("elementDesc is null");
            return;
        }
        String key = getKey(elementDesc.getFileType(), elementDesc.getName(), elementDesc.getLayer());
        if(NAME_ELEMENTDESC_MAP.get(key) != null) {
            System.out.println("已经存在该元素" + key);
            return;
        }
        NAME_ELEMENTDESC_MAP.put(key, elementDesc);
    }

    @NotNull
    private static String getKey(String fileType, String elementName, int elementLayer) {
        if ((StringUtils.isEmpty(elementName) || elementLayer < 0)) {
            System.out.println("元素名称 或 元素层级为空 "+fileType + "_" +elementName +"_"+ elementLayer);
            return null;
        }
        return fileType == null? "" : fileType + "_" + elementName +"_" + elementLayer;
    }

    /**
     * 获取元素
     * @param elementName
     * @param layer
     * @return
     */
    public static ElementDesc getElementDesc(String xmlFileType, String elementName, int layer) {
        return NAME_ELEMENTDESC_MAP.get(getKey(xmlFileType, elementName, layer));
    }
}
